package org.oy.sealogger.ds;

import java.util.Collection;
import java.util.Vector;

import org.oy.sealogger.device.Device;

/**
 * Data source that filters the incoming <b>character</b> data and only notifies its
 * observers if it finds something the client is interested in.
 * @author riku
 *
 */
public abstract class FilteringDataSource extends DataSource {

	public static final byte FILTER_MODE_EXCLUSIVE = 0;
	public static final byte FILTER_MODE_INCLUSIVE = 1;
	
	public static final byte FILTER_MODE_DEFAULT = FILTER_MODE_INCLUSIVE;
	
	protected Collection<String> filters = new Vector<String>();
	protected byte mode = FILTER_MODE_DEFAULT;
	
	/**
	 * Constructs a new instance of data source setting it automatically
	 * to inclusive filtering mode. A data source created using 
	 * this constructor will not do anything before it has been attached
	 * to a device by using the attach() method.
	 */
	public FilteringDataSource() {}
	
	/**
	 * Constructs a new instance of data source setting it automatically
	 * to inclusive filtering mode. A data source created using this constructor
	 * will not do anything before it has been attached to a device by using the
	 * attach() method.
	 * @param terminateOnError true means the thread will terminate on I/O error
	 * with the device if the connection is lost for some reason
	 */
	public FilteringDataSource(boolean terminateOnError) {
		this.terminateOnError = terminateOnError;
	}
	
	/**
	 * Constructs a new instance of data source setting it automatically
	 * to inclusive filtering mode. If this constructor is used, the attach() 
	 * method must <b>not</b> be run separately unless you want to change the
	 * input device.
	 * @param device The device
	 * with the device if the connection is lost for some reason
	 */
	public FilteringDataSource(Device device) {
		this.device = device;
	}
	
	/**
	 * Constructs a new instance of data source setting it automatically to
	 * inclusive filtering mode. If this constructor is used, the attach() method
	 * must <b>not</b> be run separately unless you want to change the input device.
	 * @param device the device
	 * @param terminateOnError true means the thread will terminate on I/O error
	 */
	public FilteringDataSource(Device device, boolean terminateOnError) {
		this.device = device;
		this.terminateOnError = terminateOnError;
	}
	
	/**
	 * Constructs a new instance of NMEA0183DataSource
	 * @param device GPS device
	 * @param mode MODE_INCLUSIVE means that only the codes that match the set filters will trigger action
	 * whereas MODE_EXCLUSIVE means all the rows that do <b>not</b> match the set filters will trigger action.
	 * Any mode value above zero will set exclusive mode, 0 and below inclusive.
	 */
	public FilteringDataSource(Device device, int mode) {
		this.device = device;
		setMode(mode);
	}
	
	/**
	 * Add an NMEA code filter.
	 * @param nmeaCode
	 */
	public void addFilter(String filter) {
		this.filters.add(filter);
	}
	
	/**
	 * Add a collection of NMEA code filters.
	 * @param nmeaCodes
	 */
	public void addFilter(Collection<String> filters) {
		this.filters.addAll(filters);
	}
	
	/**
	 * Remove a filter.
	 * @param code
	 */
	public void removeFilter(String filter) {
		filters.remove(filter);
	}
	
	/**
	 * Remove a collection of filters.
	 * @param nmeaCodes
	 */
	public void removeFilter(Collection<String> filters) {
		this.filters.removeAll(filters);
	}
	
	public void clearFilters() {
		filters.clear();
	}
	
	/**
	 * Ask if the filtering is done exclusively
	 * @return true when mode is set to MODE_EXCLUSIVE
	 */
	public boolean filteringExclusively() {
		return this.mode == FILTER_MODE_EXCLUSIVE;
	}
	
	/**
	 * Get the current filtering mode.
	 * @return either the value of DataSource.FILTER_MODE_INCLUSIVE 
	 * or DataSource.FILTER_MODE_EXCLUSIVE
	 */
	public byte getMode() {
		return this.mode;
	}
	
	/**
	 * Change the current filtering mode.
	 * @param mode Anything greater than zero will set inclusive filter mode, otherwise exclusive
	 */
	public void setMode(int mode) {
		this.mode = mode > 0 ? FILTER_MODE_INCLUSIVE : FILTER_MODE_EXCLUSIVE;
	}
	
	public void setRetryOnError(boolean terminateOnError) {
		this.terminateOnError = terminateOnError;
	}
	
}
